#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>

static struct kprobe kp = {
	.symbol_name = "c_read",
};

static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
	int i = 0;
	printk("Pre ModuleName: %s\n", p->symbol_name);

#ifdef CONFIG_32BIT
	printk("\tpad0: 0x%08lx 0x%08lx 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", regs->pad0[0], regs->pad0[1], regs->pad0[2], regs->pad0[3], regs->pad0[4], regs->pad0[5]);
#endif

	for (i = 0; i< 8; i++) {
		printk("\tregs[%d]: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", i, regs->regs[0 + 4*i], regs->regs[1 + 4*i], regs->regs[2 + 4*i], regs->regs[3 + 4*i]);
	}

	/* Saved special registers. */
	printk("\tcp0_status: 0x%08lx\n", regs->cp0_status);
	printk("\thi: 0x%08lx\n", regs->hi);
	printk("\tlo: 0x%08lx\n", regs->lo);
#ifdef CONFIG_CPU_HAS_SMARTMIPS
	printk("\tacx: 0x%08lx\n", regs->acx);
#endif
	printk("\tcp0_badvaddr: 0x%08lx\n", regs->cp0_badvaddr);
	printk("\tcp0_cause: 0x%08lx\n", regs->cp0_cause);
	printk("\tcp0_epc: 0x%08lx\n", regs->cp0_epc);
#ifdef CONFIG_MIPS_MT_SMTC
	printk("\tcp0_tcstatus: 0x%08lx\n", regs->cp0_tcstatus);
#endif /* CONFIG_MIPS_MT_SMTC */
#ifdef CONFIG_CPU_CAVIUM_OCTEON
	printk("\tmpl: 0x%08lx 0x%08lx 0x%08lx\n", regs->mpl[0], regs->mpl[1], regs->mpl[2]);
	printk("\tmtp: 0x%08lx 0x%08lx 0x%08lx\n", regs->mtp[0], regs->mtp[1], regs->mtp[2]);
#endif
	printk("\n");
	return 0;
}

static void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
	int i = 0;
	printk("Pos ModuleName: %s\n", p->symbol_name);

#ifdef CONFIG_32BIT
	printk("\tpad0: 0x%08lx 0x%08lx 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", regs->pad0[0], regs->pad0[1], regs->pad0[2], regs->pad0[3], regs->pad0[4], regs->pad0[5]);
#endif

	for (i = 0; i< 8; i++) {
		printk("\tregs[%d]: 0x%08lx 0x%08lx 0x%08lx 0x%08lx\n", i, regs->regs[0 + 4*i], regs->regs[1 + 4*i], regs->regs[2 + 4*i], regs->regs[3 + 4*i]);
	}

	/* Saved special registers. */
	printk("\tcp0_status: 0x%08lx\n", regs->cp0_status);
	printk("\thi: 0x%08lx\n", regs->hi);
	printk("\tlo: 0x%08lx\n", regs->lo);
#ifdef CONFIG_CPU_HAS_SMARTMIPS
	printk("\tacx: 0x%08lx\n", regs->acx);
#endif
	printk("\tcp0_badvaddr: 0x%08lx\n", regs->cp0_badvaddr);
	printk("\tcp0_cause: 0x%08lx\n", regs->cp0_cause);
	printk("\tcp0_epc: 0x%08lx\n", regs->cp0_epc);
#ifdef CONFIG_MIPS_MT_SMTC
	printk("\tcp0_tcstatus: 0x%08lx\n", regs->cp0_tcstatus);
#endif /* CONFIG_MIPS_MT_SMTC */
#ifdef CONFIG_CPU_CAVIUM_OCTEON
	printk("\tmpl: 0x%08lx 0x%08lx 0x%08lx\n", regs->mpl[0], regs->mpl[1], regs->mpl[2]);
	printk("\tmtp: 0x%08lx 0x%08lx 0x%08lx\n", regs->mtp[0], regs->mtp[1], regs->mtp[2]);
#endif
	printk("\n");
}

static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
	printk("fault_handler: p->addr = %pF, trap #%dn", p->addr, trapnr);
	return 0;
}

static int __init kprobe_init(void)
{
	int ret;
	kp.pre_handler = handler_pre;
	kp.post_handler = handler_post;
	kp.fault_handler = handler_fault;

	ret = register_kprobe(&kp);
	if (ret < 0) {
		pr_err("register_kprobe failed, returned %d\n", ret);
		return ret;
	}
	pr_info("Planted kprobe at %pF\n", kp.addr);
	return 0;
}

static void __exit kprobe_exit(void)
{
	unregister_kprobe(&kp);
	pr_info("kprobe at %pF unregistered\n", kp.addr);
}

module_init(kprobe_init);
module_exit(kprobe_exit);
MODULE_LICENSE("GPL");
